﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.XPath;

namespace XML2Location
{
	enum XMLElement
	{
		LocationList,
		Location,
		Country,
		State,
		Name,
	}

	enum XMLAttribute
	{
		Latitude,
		Longitude,
		Country,
		State,
		ID,
	}

	internal static class ReaderExtensions
	{
		const string NS = "urn:denebsoft-skymap-location";

		internal static XPathNavigator FindChildren(this XPathNavigator reader, XMLElement name, string id)
		{
			if (String.IsNullOrEmpty(id)) return null;
			var nsman = new XmlNamespaceManager(reader.NameTable);
			nsman.AddNamespace("tmp", NS);
			return reader.SelectSingleNode(String.Format("tmp:{0}[@{1} = '{2}']", name, XMLAttribute.ID, id), nsman);
		}

		internal static void ForEachChildren(this XPathNavigator reader, XMLElement name, Action<XPathNavigator> body)
		{
			var childs = reader.SelectChildren(name.ToString(), NS);
			foreach (XPathNavigator child in childs)
				body(child);
		}

		internal static void ForEachChildren(this XPathNavigator reader, XMLElement name, Action<int> count, Action<XPathNavigator> body)
		{
			var childs = reader.SelectChildren(name.ToString(), NS);
			count(childs.Count);
			foreach (XPathNavigator child in childs)
				body(child);
		}

		internal static double GetAttributeDouble(this XPathNavigator reader, XMLAttribute name)
		{
			string value = reader.GetAttribute(name.ToString(), String.Empty);
			if (String.IsNullOrEmpty(value))
				throw new ArgumentException(name.ToString());
			return XmlConvert.ToDouble(value);
		}

		internal static string GetAttribute(this XPathNavigator reader, XMLAttribute name)
		{
			return reader.GetAttribute(name.ToString(), String.Empty);
		}

		internal static string GetAttributeLanguage(this XPathNavigator reader)
		{
			return reader.XmlLang;
		}

		internal static string GetName(this XPathNavigator reader, string lang)
		{
			string first = null;
			string rus = null;
			reader.ForEachChildren(XMLElement.Name, name =>
			{
				if (first == null)
					first = name.Value;
				if (rus == null && name.GetAttributeLanguage() == "ru")
					rus = name.Value;
			});
			return rus ?? first;
		}
	}

	internal static class WriterExtensions
	{
	}

	class Program
	{
		static void Main(string[] args)
		{
			string ifn = args.Length == 0 || Path.GetExtension(args[0]) == ".dat" ? "location.xml" : args[0];
			string ofn = args.Length == 1 && Path.GetExtension(args[0]) == ".dat" ? args[0] : args.Length <= 1 ? "location.dat" : args[1];

			if (args.Length > 2 || Path.GetExtension(ifn) != ".xml" || Path.GetExtension(ofn) != ".dat" ||
				args.Length > 1 && Path.GetExtension(args[0]) == ".dat")
			{
				Console.WriteLine("usage: {0} [location.xml] [location.dat]", Path.GetFileNameWithoutExtension(System.Reflection.Assembly.GetExecutingAssembly().Location));
				return;
			}

			var doc = new XPathDocument(ifn);
			var reader = doc.CreateNavigator();
			using (var ofs = new FileStream(ofn, FileMode.Create))
			using (var writer = new BinaryWriter(ofs))
			{
				reader.MoveToFirstChild(); //LocationList

				reader.ForEachChildren(XMLElement.Location, count => writer.Write(count), loc =>
				{
					double Latitude = loc.GetAttributeDouble(XMLAttribute.Latitude);
					double Longitude = loc.GetAttributeDouble(XMLAttribute.Longitude);
					string Country = loc.GetAttribute(XMLAttribute.Country);
					string State = loc.GetAttribute(XMLAttribute.State);
					string Name = loc.GetName("ru");
					var country = reader.FindChildren(XMLElement.Country, Country);
					var state = country.FindChildren(XMLElement.State, State);
					Country = country.GetName("ru");
					if (state != null)
						State = state.GetName("ru");

					writer.Write(Name);
					writer.Write(Country);
					writer.Write(State);
					writer.Write(Latitude);
					writer.Write(Longitude);
				});
			}
		}
	}
}
